package
{
	import __AS3__.vec.Vector;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Graphics;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.filters.ColorMatrixFilter;
	import flash.geom.Point;
	
	import frocessing.color.ColorRGB;

//	[SWF (backgroundColor = "0x0", frameRate = "30", width = "465", height = "465")]
	public class Bezier extends Sprite
	{
		public const NUM_POINTS:int = 5;
		public const NUM_LOOPS:int = 2;
		
		public var colors:Vector.<ColorRGB> = new Vector.<ColorRGB>();
		public var pts:Vector.<Vector.<Point>> = new Vector.<Vector.<Point>>();
		public var vel:Vector.<Vector.<Point>> = new Vector.<Vector.<Point>>();
		
		public var val:Number = .9;
		public var fade:ColorMatrixFilter = new ColorMatrixFilter([val,0,0,0,0,
																   0,val,0,0,0,
																   0,0,val,0,0,
																   0,0,0,1,0]);
		public var bmd:BitmapData = new BitmapData(465, 465, false, 0x0);
		public var bmp:Bitmap = new Bitmap(bmd);
		public var mc:Sprite = new Sprite();
		
		public function Bezier()
		{
			var i:int=0;
			addChild(bmp);
			for(var j:int=0; j<NUM_LOOPS; j++)
			{
				colors[j] = new ColorRGB((j&1)*255, Math.round(Math.random())*255, ((j+1)&1)*255);
				pts[j] = new Vector.<Point>();
				vel[j] = new Vector.<Point>();
				for(i=0; i<NUM_POINTS; i++)
				{
					pts[j][i] = new Point(465*Math.random() >> 0, 465*Math.random() >> 0);
					vel[j][i] = new Point(Math.random()*8-4, Math.random()*8-4);
				}
			}
			
			draw();
			
			stage.addEventListener(Event.ENTER_FRAME, onEnter);
		}
		
		public function onEnter(event:Event):void
		{
			var c:ColorRGB;
			var i:int=0;
			for(var j:int=0; j<NUM_LOOPS; j++)
			{
				for(i=0; i<NUM_POINTS; i++)
				{
					pts[j][i].x += vel[j][i].x;
					if(pts[j][i].x > 465)
					{
						pts[j][i].x -= pts[j][i].x-465;
						vel[j][i].x *= -1;
					}
					else if(pts[j][i].x < 0)
					{
						pts[j][i].x *= -1;
						vel[j][i].x *= -1;
					}
					
					pts[j][i].y += vel[j][i].y;
					if(pts[j][i].y > 465)
					{
						pts[j][i].y -= pts[j][i].y-465;
						vel[j][i].y *= -1;
					}
					else if(pts[j][i].y < 0)
					{
						pts[j][i].y *= -1;
						vel[j][i].y *= -1;
					}
				}
				
				c = colors[j];
				if(c.r == 255)
				{
					if(c.b > 0) c.b--;
					else if(c.g < 255) c.g++;
					else c.r--;
				}
				else if(c.g == 255)
				{
					if(c.r > 0) c.r--;
					else if(c.b < 255) c.b++;
					else c.g--;
				}
				else if(c.b == 255)
				{
					if(c.g > 0) c.g--;
					else if(c.r < 255) c.r++;
					else c.b--;
				}
			}
			
			draw();
		}
		
		public function drawClosedLoop(pnts:Vector.<Point>, accuracy:int=20):void
		{
			var p:Vector.<Point> = new Vector.<Point>(3,true);
			for(var i:int=0; i<pnts.length-2; i++)
			{
				p[0] = Point.interpolate(pnts[i], pnts[i+1], .5);
				p[1] = pnts[i+1];
				p[2] = Point.interpolate(pnts[i+1], pnts[i+2], .5);
				drawCurve(p, accuracy);
			}
			
			p[0] = Point.interpolate(pnts[i], pnts[i+1], .5);
			p[1] = pnts[i+1];
			p[2] = Point.interpolate(pnts[i+1], pnts[0], .5);
			drawCurve(p, accuracy);
			
			i++;
			
			p[0] = Point.interpolate(pnts[i], pnts[0], .5);
			p[1] = pnts[0];
			p[2] = Point.interpolate(pnts[0], pnts[1], .5);
			drawCurve(p, accuracy);
		}
		
		public function drawCurve(pnts:Vector.<Point>, accuracy:int=20):void
		{
			var g:Graphics = mc.graphics;
			var a:int;
			var p:Point;
			
			g.moveTo(pnts[0].x, pnts[0].y);
			for(a=0; a <= accuracy; a++)
			{
				p = getPointAt(pnts, a/accuracy);
				g.lineTo(p.x, p.y);
			}
		}
		
		protected function getPointAt(pnts:Vector.<Point>, t:Number):Point
		{
			var n:int = pnts.length-1;
			var m:Number = Math.pow((1-t), n);
			var p:Point = new Point(pnts[0].x * m, pnts[0].y * m);
			for(var i:int=1; i<=n; i++)
			{
				m = Math.pow((1-t), n-i)*bc(n, i)*Math.pow(t, i);
				p = p.add(new Point(pnts[i].x * m, pnts[i].y * m));
			}
			return p;
		}
		
		protected function bc(n:int, k:int):int // Binomial Coefficient
		{
			return (f(n)/(f(k)*f(n-k)));
		}
		
		protected function f(value:int):int // Factorial
		{
			var i:int = value;
			var result:int = 1;
			
			for(; i>0; i--)
			{
				result *= i;
			}
			
			return result;
		}
		
		protected function draw():void
		{
			mc.graphics.clear();
			for(var j:int=0; j<NUM_LOOPS; j++)
			{
				mc.graphics.lineStyle(1,colors[j].value);
				drawClosedLoop(pts[j]);
				mc.graphics.endFill();
			}
			
			bmd.applyFilter(bmd, bmd.rect, new Point(), fade);
			bmd.draw(mc);
		}
	}
}